﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DatabaseExtension.ConnectionString
{
    public class OleDbConnectionInfo : ConnectionInfo
    {
        /// <summary>
        /// Get the list with all available drivers on the machine.
        /// </summary>
        public static IEnumerable<AvailableEngines> OleDbEngines
        {
            get
            {
                using (var reader = System.Data.OleDb.OleDbEnumerator.GetRootEnumerator())
                {
                    while (reader.Read())
                    {
                        yield return new AvailableEngines { Name = reader["SOURCES_NAME"].ToString(), Description = reader["SOURCES_DESCRIPTION"].ToString() };
                    }
                }
            }
        }
        /// <summary>
        /// Contain information about a single OleDb Engine.
        /// </summary>
        public class AvailableEngines
        {
            /// <summary>
            /// Source Name
            /// </summary>
            public String Name;
            /// <summary>
            /// Source Description
            /// </summary>
            public String Description;
            /// <summary>
            /// Compare object with current instance.
            /// </summary>
            /// <param name="obj">object to compare</param>
            /// <returns>True if both have the same name.</returns>
            public override bool Equals(object obj)
            {
                Boolean result = false;
                var type = obj == null ? null : obj.GetType();
                if (type != null)
                {
                    if (Type.Equals(type, typeof(String)))
                        result = String.Equals(this.Name, obj.ToString());
                    if (Type.Equals(type, typeof(AvailableEngines)))
                        result = String.Equals(this.Name, (obj as AvailableEngines).Name);
                }
                return result;
            }
            /// <summary>
            /// Get Hash code
            /// </summary>
            /// <returns></returns>
            public override int GetHashCode()
            {
                return HashHelper.GetHashCode(this.Name);
            }
        }
        /// <summary>
        /// Provide configuration for extended properties.
        /// </summary>
        public class ExtendedProperties
        {
            /// <summary>
            /// Import Mixed Type. Set to 1 to get safer result set.
            /// </summary>
            public byte? Imex;
            /// <summary>
            /// If true, document contains header row.
            /// </summary>
            public Boolean? Header;
            /// <summary>
            /// Information about Extended Property data type
            /// </summary>
            public DatabaseExtension.Enums.OleDb.OleDbDocumentType DocumentType;
            /// <summary>
            /// Information about data delimeter.
            /// </summary>
            public DatabaseExtension.Enums.OleDb.OleDbFomatDelimiter DelimiterType;
            /// <summary>
            /// Get or Set user defined delimiter.
            /// Set delimiter to null will set DelimiterType to None,
            /// else DelimiterType will be set to DelimitedByX
            /// </summary>
            public String Delimiter
            {
                get
                {
                    return this._Delimiter;
                }
                set
                {
                    if (String.IsNullOrEmpty(value) == false)
                        this.DelimiterType = Enums.OleDb.OleDbFomatDelimiter.DelimitedByX;
                    else
                        this.DelimiterType = Enums.OleDb.OleDbFomatDelimiter.None;
                    this._Delimiter = value;
                }
            }
            private String _Delimiter;
            /// <summary>
            /// Extended Property information.
            /// </summary>
            /// <returns></returns>
            public override string ToString()
            {
                String result = null;
                char delimiter = ';';
                if (this.DocumentType == Enums.OleDb.OleDbDocumentType.None)
                    throw new ArgumentException("Document type cannot be undefined.");
                /// Write document Type and Format
                result = String.Concat(result, this.DocumentType.EnumToString(), delimiter, "FMT=");
                /// End Write Format Type
                switch (this.DelimiterType)
                {
                    case Enums.OleDb.OleDbFomatDelimiter.DelimitedByX:
                        if (String.IsNullOrEmpty(this.Delimiter) == true)
                            throw new ArgumentNullException("Delimeter cannot be null for DelimitedByX");
                        result = String.Concat(result, "Delimited(", this.Delimiter, ")", delimiter);
                        break;
                    case Enums.OleDb.OleDbFomatDelimiter.None:
                        break;
                    default:
                        result = String.Concat(result, this.DelimiterType.EnumToString(), delimiter);
                        break;
                }
                /// Write Header
                if (this.Header.HasValue == true)
                    result = String.Concat(result, "HDR=", this.Header.Value == true ? "YES" : "NO", delimiter);
                if (this.Imex.HasValue == true)
                    result = String.Concat(result, "IMEX=", Imex.Value, delimiter);
                ///Remove options delimeter
                result = result.Substring(0, result.Length - 1);
                return String.Concat("'", result, "'");
            }
        }
        /// <summary>
        /// Get the connection String.
        /// </summary>
        public override string ConnectionString
        {
            get
            {
                if (this.CheckProviderAccessibility == true && 
                    String.IsNullOrEmpty(this.Provider) == false && 
                    OleDbConnectionInfo.OleDbEngines.Where(a => AvailableEngines.Equals(a, this.Provider)).Count() < 1)
                    throw new ArgumentException(String.Concat("Could not find (", this.Provider, ") OleDb provider."));

                return base.ConnectionString;
            }
        }
        /// <summary>
        /// Get the OleDB Original ConnectionStringBuilder
        /// </summary>
        public System.Data.OleDb.OleDbConnectionStringBuilder OleDbConnectionBuilder
        {
            get
            {
                return base.ConnectionStringBuilder as System.Data.OleDb.OleDbConnectionStringBuilder;
            }
        }
        #region ConnectionStringBuilder Derived Parameter
        /// <summary>
        /// Gets or sets the name of the data source to connect to.
        /// </summary>
        public String DataSource
        {
            get
            {
                return this.OleDbConnectionBuilder.DataSource;
            }
            set
            {
                this.OleDbConnectionBuilder.DataSource = value;
            }
        }
        /// <summary>
        /// Gets or sets the name of the Universal Data Link (UDL) file for connecting to the data source.
        /// </summary>
        public String FileName
        {
            get
            {
                return this.OleDbConnectionBuilder.FileName;
            }
            set
            {
                this.OleDbConnectionBuilder.FileName = value;
            }
        }
        /// <summary>
        /// Gets or sets the value to be passed for the OLE DB Services key within the connection string.
        /// <seealso cref="http://msdn.microsoft.com/de-AT/library/system.data.oledb.oledbconnectionstringbuilder.oledbservices.aspx"/>
        /// </summary>
        public int OleDbServices
        {
            get
            {
                return this.OleDbConnectionBuilder.OleDbServices;
            }
            set
            {
                this.OleDbConnectionBuilder.OleDbServices = value;
            }
        }
        /// <summary>
        /// Gets or sets a Boolean value that indicates whether security-sensitive information, such as the password, is returned as part of the connection if the connection is open or has ever been in an open state.
        /// </summary>
        public Boolean PersistSecurityInfo
        {
            get
            {
                return this.OleDbConnectionBuilder.PersistSecurityInfo;
            }
            set
            {
                this.OleDbConnectionBuilder.PersistSecurityInfo = value;
            }
        }
        /// <summary>
        /// Gets or sets a string that contains the name of the data provider associated with the internal connection string.
        /// </summary>
        public String Provider
        {
            get
            {
                return this.OleDbConnectionBuilder.Provider;
            }
            set
            {
                this.OleDbConnectionBuilder.Provider = value;
            }
        }
        #endregion
        /// <summary>
        /// If true, check the current provider before creating Connection String.
        /// </summary>
        public Boolean CheckProviderAccessibility;
        /// <summary>
        /// Get or Set extended properties.
        /// </summary>
        public ExtendedProperties OleDbExtendedProperties
        {
            get
            {
                return this._OleDbExtendedProperties;
            }
            set
            {
                Boolean containKey = this.OleDbConnectionBuilder.ContainsKey("Extended Properties");
                if (value == null && containKey == true)
                    this.OleDbConnectionBuilder.Remove("Extended Properties");
                else if (value != null)
                {
                    if (containKey == false)

                        this.OleDbConnectionBuilder.Add("Extended Properties", value.ToString());
                    else
                        this.OleDbConnectionBuilder["Extended Properties"] = value.ToString();
                }
                this._OleDbExtendedProperties = value;
            }
        }
        private ExtendedProperties _OleDbExtendedProperties;
        /// <summary>
        /// Create bew OleDbConnectionInfor from OleDbConnectionStringBuilder
        /// </summary>
        /// <param name="oledbConnectionString"></param>
        public OleDbConnectionInfo(System.Data.OleDb.OleDbConnectionStringBuilder oledbConnectionString)
            : base(Databases.OleDb, oledbConnectionString)
        {
        }
        /// <summary>
        /// Create new OleDbConnectionInfo based on DataSource, User and password
        /// Note DataSource is not Provider.
        /// </summary>
        /// <param name="dataSource">Data Source of current Connection</param>
        /// <param name="user">User ID</param>
        /// <param name="password">Password</param>
        public OleDbConnectionInfo(String dataSource, String user, String password)
            : base(Databases.OleDb, dataSource, user, password)
        {
        }

        /// <summary>
        /// Override base method that throw NotImplementedException.
        /// </summary>
        /// <returns>returns OleDbConnectionStringbuilder</returns>
        protected override System.Data.Common.DbConnectionStringBuilder GetConnectionStringbuilder()
        {
            this.ClientFactory = System.Data.OleDb.OleDbFactory.Instance;
            var result = this.ClientFactory.CreateConnectionStringBuilder() as System.Data.OleDb.OleDbConnectionStringBuilder;
            result.DataSource = this.Server;
            if (String.IsNullOrEmpty(this.UserName) == false && result.ContainsKey("User ID") == false)
                result["User ID"] = this.UserName;
            if (String.IsNullOrEmpty(this.Password) == false && result.ContainsKey("Password") == false)
                result["Password"] = this.Password;
            return result;
        }
        /// <summary>
        /// Create new Db Parameter
        /// </summary>
        /// <param name="name">Parameter Name</param>
        /// <param name="value">Parameter Value</param>
        /// <param name="type">Use System.Data.DbType for inserted parameter</param>
        /// <param name="isNullable">if true, empty object will be converted to DbNull.Value</param>
        /// <param name="Size">Object Size.</param>
        /// <returns>Return new DbParameter</returns>
        public override System.Data.Common.DbParameter CreateParameter(string name, object value, object type, bool isNullable, int Size = 0)
        {
            var param = this.ClientFactory.CreateParameter();
            param.ParameterName = name;
            param.Value = (isNullable && value == null) ? DBNull.Value : value;
            param.Size = Size;
            if (type is System.Data.DbType)
                param.DbType = (System.Data.DbType)type;
            else
                throw new ArgumentException("Could not recognize Db Type");
            param.IsNullable = isNullable;
            return param;
        }
        /// <summary>
        /// Create new Db Parameter. Autodetect parameter value type
        /// </summary>
        /// <param name="name">Parameter Name</param>
        /// <param name="value">Parameter Value</param>
        /// <param name="isNullable">if true, empty object will be converted to DbNull.Value</param>
        /// <param name="Size">Object Size.</param>
        /// <returns>Return new DbParameter</returns>
        public override System.Data.Common.DbParameter CreateParameter(string name, object value, bool isNullable, int Size = 0)
        {
            return this.CreateParameter(name, value, DbTypeConverter.ToDbType(value), isNullable, Size);
        }
    }
}
